using System;
using System.Text;

namespace Altairis.Web {

    public static class Convert {
        private const string BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";

        /// <summary>
        /// Converts a Base32-k string into an array of bytes.
        /// </summary>
        /// <exception cref="System.ArgumentException">
        /// Input string <paramref name="s">s</paramref> contains invalid Base32-k characters.
        /// </exception>
        public static byte[] FromBase32String(string str) {
            int numBytes = str.Length * 5 / 8;
            byte[] bytes = new Byte[numBytes];

            // all UPPERCASE chars
            str = str.ToUpper();

            int bit_buffer;
            int currentCharIndex;
            int bits_in_buffer;

            if (str.Length < 3) {
                bytes[0] = (byte)(BASE32_ALPHABET.IndexOf(str[0]) | BASE32_ALPHABET.IndexOf(str[1]) << 5);
                return bytes;
            }

            bit_buffer = (BASE32_ALPHABET.IndexOf(str[0]) | BASE32_ALPHABET.IndexOf(str[1]) << 5);
            bits_in_buffer = 10;
            currentCharIndex = 2;
            for (int i = 0; i < bytes.Length; i++) {
                bytes[i] = (byte)bit_buffer;
                bit_buffer >>= 8;
                bits_in_buffer -= 8;
                while (bits_in_buffer < 8 && currentCharIndex < str.Length) {
                    bit_buffer |= BASE32_ALPHABET.IndexOf(str[currentCharIndex++]) << bits_in_buffer;
                    bits_in_buffer += 5;
                }
            }

            return bytes;
        }

        /// <summary>
        /// Converts an array of bytes to a Base32-k string.
        /// </summary>
        public static string ToBase32String(byte[] data) {
            StringBuilder sb = new StringBuilder();         // holds the base32 chars
            byte index;
            int hi = 5;
            int currentByte = 0;

            while (currentByte < data.Length) {
                // do we need to use the next byte?
                if (hi > 8) {
                    // get the last piece from the current byte, shift it to the right
                    // and increment the byte counter
                    index = (byte)(data[currentByte++] >> (hi - 5));
                    if (currentByte != data.Length) {
                        // if we are not at the end, get the first piece from
                        // the next byte, clear it and shift it to the left
                        index = (byte)(((byte)(data[currentByte] << (16 - hi)) >> 3) | index);
                    }

                    hi -= 3;
                }
                else if (hi == 8) {
                    index = (byte)(data[currentByte++] >> 3);
                    hi -= 3;
                }
                else {
                    // simply get the stuff from the current byte
                    index = (byte)((byte)(data[currentByte] << (8 - hi)) >> 3);
                    hi += 5;
                }

                sb.Append(BASE32_ALPHABET[index]);
            }

            return sb.ToString();
        }

        /// <summary>
        /// Encodes data using Base64 method in URL safe variants (replaces <c>+</c> with <c>-</c> and <c>/</c> with <c>_</c>).
        /// </summary>
        /// <param name="data">Data to be encoded.</param>
        /// <returns>Encoded data with padding removed.</returns>
        public static string ToUrlSafeBase64String(byte[] data) {
            return ToUrlSafeBase64String(data, true);
        }

        /// <summary>
        /// Encodes data using Base64 method in URL safe variants (replaces <c>+</c> with <c>-</c> and <c>/</c> with <c>_</c>).
        /// </summary>
        /// <param name="data">Data to be encoded.</param>
        /// <param name="stripPadding">If <c>true</c>, end padding <c>=</c> are removed.</param>
        /// <returns>Encoded data.</returns>
        public static string ToUrlSafeBase64String(byte[] data, bool stripPadding) {
            string ret = System.Convert.ToBase64String(data);
            ret = ret.Replace('+', '-');
            ret = ret.Replace('/', '_');
            if (stripPadding) ret = ret.TrimEnd('=');
            return ret;
        }
    }
}